Memory management is one of the most difficult, most error-prone, but also most important aspects of object-oriented programming. An object-oriented program implemented in C++, for example, is based around a collection of objects which represent either a physical or abstract part of the world which the program addresses. When the program runs, memory must be allocated for those objects. The C++ language has two operators, "new" and "delete", related to memory allocation. The programmer uses operator "new" to allocate memory for an object and uses operator "delete" to free up the allocated memory when the object is no longer needed by the program. It may be seen that the task of memory management is primarily a decision of when to call operator "delete".
Objects within the program may refer to each other by having pointers to the allocated memory. For example, an employee object may have a pointer to the memory allocated for the object representing that employee's manager. Objects can send messages to the object they point to, so that they can receive information and give commands from and to the object. These operations may result in a change in the memory location pointed to. Several memory management problems related to the use of pointers require careful attention and consideration by the programmer. These problems include wild pointers, limited memory, and memory leaks.
The central problem with memory management is that an object with a pointer to another object is not notified when that object is deleted so that the pointer is no longer valid. This is known as a wild pointer problem in the computer programming industry. If an object then sends a message to the now deleted object via the wild pointer, the results are often unpredictable. The program may crash immediately, the results may be invalid, or, as is frequently the case, the method on the wild pointer starts a chain reaction of invalid operations that leads to a crash at a later point in the program. It is extraordinarily difficult to debug such occurrences. There is a saying in the industry: "the result of even one wild pointer is pain, misery, and death." (C++ FAQs by Cline and Lomow, pg 324).
One way to avoid wild pointers is never to call operator "delete". This may be a valid solution only if the program has an unlimited amount of memory for its use. Typically, a program will deal with many more objects during the entire run of the program than it needs at any one time. Therefore, it is impractical and unrealistic to continue to maintain memory space for unused objects.
Even if there is enough memory to allocate all objects an application needs, if some objects allocated by operator "new" during program execution are not deleted at some point during program execution, the allocated memory remains unavailable for other programs or additional runs of the same program until the computer is restarted. This problem is commonly called a memory leak. When a program with a memory leak is run repeatedly, eventually the leak is so severe that other programs will crash or new programs cannot be started even if no other program is running.
To avoid the problems of wild pointers, limited memory, and memory leaks, the programmer must delete each object at the right time. Determining when is the right time is very difficult. Although C++ provides one basic way of allocating dynamic memory, the allocated objects are used for diverse reasons. The "right time" to delete the objects depends on the way these objects and pointers pointing to them are used.
One use for pointers is to point to one object which may be a part of another object. For example, a video tape may have several video segments recorded on it, so each video segment object is a part of a video tape object. In order to know which tape object a video segment object is a part of, the video segment object may have a back pointer to a tape object. It may be seen that when a video tape object is deleted, all video segment objects that have a back pointer to it should also be deleted to avoid generating wild pointers.
Another difficulty is posed when one object may have a list of objects of another type. A video tape object may include a list of video segment objects it contains. Additionally, a planned broadcast object may have a list of video segment objects residing on one or more video tape objects that will be shown during the course of the broadcast. Further, the same video segments may be used for several broadcasts, and therefore are listed on several broadcast lists at the same time. Each list that contains an object has a pointer to that object. When an object containing a list is deleted, the list is also deleted. The difficult memory management decision, however, is whether the individual objects on the list should also be deleted. In this example, when a broadcast object is deleted from memory, the video segment objects may not be, in general, deleted. However, when a video tape object is deleted, all the video segment objects on its list should also be deleted. The C++ language does not provide any explicit syntax for making the distinction between these two types of relationships. The programmer must provide explicit code to properly distinguish and treat these two cases differently or wild pointers may result.
A further difficulty is posed when an object may also be temporarily related to another object. For example, a video cassette recorder (VCR) may be playing a video tape. In this case, the VCR object has a pointer to the video tape object. Syntactically, this relationship appears the same as the back pointer that a video segment object has to its video tape. However, unlike the back pointer example, the VCR object should not be deleted when the video tape object is deleted. In fact, the VCR object may point to different video tape objects at different points in the broadcast, or may not point to any video tape object at all. This difference in relationship requires distinction and different memory management strategies.
In all the above examples, the objects pointed to are sharable; that is, many objects may have pointers to the same object. Another common use of pointers is for an object that is owned by another object and should only be pointed to by the owning object. For example, a VCR object may have a strategy for what it does when it reaches the end of a tape: rewind the tape and play it again, rewind it and eject it, or just stop. In object-oriented programming, objects can represent actions such as these end-of-tape strategies. Therefore, the VCR object has a pointer to a strategy object, which may change during the course of a broadcast. In this scenario, different VCR objects are not pointing to the same strategy objects, but a new strategy object is created for each VCR object. This relationship is called "remote ownership" and typically requires a different memory management strategy to avoid problems.
One of the biggest advantages of object-oriented programming is the ability to reuse objects in multiple applications without having to modify the original code. To allow for code reuse, new objects must be able to point to previously coded objects without change to the previously written code. Such pointers are called anonymous pointers, such that the class pointed to has no code specifically referring to the class with the pointer. Deletion of objects pointed to by anonymous pointers is another primary cause of wild pointers.
In conventional programming, memory management must be explicitly and carefully coded for each and every object. Often explicit coding is used to manage anonymous pointers. For example, when a broadcast object is deleted, the video segments it uses should not be deleted since one or more of them may be scheduled to be shown in other broadcasts. However, a video segment object should be deleted if is not needed by other broadcasts. The programmer may manage this by writing explicit code to maintain a list of all open broadcasts and their video segments, which is referred to when a broadcast is deleted. There may be other business reasons to delete an object. For example, if a video segment object is deleted because of censorship, it should be deleted from all broadcasts. The programmer is required to write specific code to check all broadcasts and delete the video segment from all broadcasts scheduled to use it. Such explicit coding goes directly against the basic object-oriented design principles of encapsulation and reuse. In addition to being time consuming and error-prone to implement, the explicit code has to be modified everywhere it is in place when the application is expanded or the objects are reused in another context.
Garbage collection is a known technique for automatically managing memory based on the concept of scope of a variable. A garbage collection product is Great Circle made by Geodesic Systems of Chicago, Ill. When memory is allocated for an object, but no currently active variable points to that object, the object can be automatically deleted without specific code written by the programmer. The disadvantage with automatic garbage collection is that it is based on the syntax of pointers in memory. For example, garbage collection schemes cannot distinguish between removing a video segment from one broadcast because it is not scheduled to be shown next time, and removing a video segment from its video tape because it is censored and should not be shown on any broadcast ever again. Explicit coding is still required to fill in this gap.
Some other techniques have been developed to solve some memory management problems but are not complete solutions. The smart pointer from the C++ compiler from Borland International Inc., Scotts Valley, Calif., for example, handles only the problem of remote ownership. Reference counting wrappers are also available to enable programmers to keep track of how many objects point to a particular object with simplified code. However, these techniques do not address all memory management problems encountered.
Currently, conventional memory management is done in a haphazard and tedious manner, which may require many sleepless hours to code and debug. Most often, software products are made available to the consumers before problems of improper memory management are completely resolved. The result is the sometimes frequent appearance of dialog windows informing users some non-specified error has occurred popping up on computer terminals and locked-up computer applications.